# author: fanchuangwater@gmail.com
# date: 2020/7/18 下午10:35
# 目的:
# 借鉴 https://github.com/Amey-Chaudhari/gesture-detection-opencv-4.x
from pykeyboard import *
from pymouse import *


import math
import sys
import time
import cv2
import numpy as np
import pygame

# *********** game stuff start **************
m = PyMouse() #建立鼠标对象
k = PyKeyboard() #建立键盘对象

pygame.init()

size = width, height = 1200, 800
speed = [1, 1]
black = 0, 0, 0

screen = pygame.display.set_mode(size)

ball = pygame.image.load("ball.gif")
ball_rect = ball.get_rect()

# *********** game stuff end   **************

cap = cv2.VideoCapture(0)
while cap.isOpened():
    # read image
    ret, img = cap.read()

    # get hand data from the rectangle sub window on the screen
    cv2.rectangle(img, (500, 500), (100, 100), (0, 255, 0), 0)
    crop_img = img[100:500, 100:500]

    # convert to grayscale
    grey = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)

    # applying gaussian blur
    value = (35, 35)
    blurred = cv2.GaussianBlur(grey, value, 0)

    # thresholdin: Otsu's Binarization method
    _, thresh1 = cv2.threshold(blurred, 127, 255,
                               cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

    # show thresholded image
    cv2.imshow('Thresholded', thresh1)

    # check OpenCV version to avoid unpacking error
    (version, _, _) = cv2.__version__.split('.')

    if version == '3':
        image, contours, hierarchy = cv2.findContours(thresh1.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    elif version == '4':
        contours, hierarchy = cv2.findContours(thresh1.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    # find contour with max area
    cnt = max(contours, key=lambda x: cv2.contourArea(x))

    # create bounding rectangle around the contour (can skip below two lines)
    x, y, w, h = cv2.boundingRect(cnt)
    cv2.rectangle(crop_img, (x, y), (x + w, y + h), (0, 0, 255), 0)

    # finding convex hull
    hull = cv2.convexHull(cnt)

    # drawing contours
    drawing = np.zeros(crop_img.shape, np.uint8)
    cv2.drawContours(drawing, [cnt], 0, (0, 255, 0), 0)
    cv2.drawContours(drawing, [hull], 0, (0, 0, 255), 0)

    # finding convex hull
    hull = cv2.convexHull(cnt, returnPoints=False)

    # finding convexity defects
    defects = cv2.convexityDefects(cnt, hull)
    count_defects = 0
    cv2.drawContours(thresh1, contours, -1, (0, 255, 0), 3)

    # applying Cosine Rule to find angle for all defects (between fingers)
    # with angle > 90 degrees and ignore defects
    for i in range(defects.shape[0]):
        s, e, f, d = defects[i, 0]

        start = tuple(cnt[s][0])
        end = tuple(cnt[e][0])
        far = tuple(cnt[f][0])

        # find length of all sides of triangle
        a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
        b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
        c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)

        # apply cosine rule
        angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) * 57

        # ignore angles > 90 and highlight rest with red dots
        if angle <= 90:
            count_defects += 1
            cv2.circle(crop_img, far, 1, [0, 0, 255], -1)
        # dist = cv2.pointPolygonTest(cnt,far,True)

        cv2.line(crop_img, start, end, [0, 255, 0], 2)
        cv2.circle(crop_img, far, 5, [0, 0, 255], -1)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    ball_rect = ball_rect.move(speed)
    if ball_rect.left < 0 or ball_rect.right > width:
        speed[0] = -speed[0]
    if ball_rect.top < 0 or ball_rect.bottom > height:
        speed[1] = -speed[1]
    #
    # define actions
    time.sleep(2)
    location1 = m.position()
    time.sleep(4)
    location2 = m.position()

    if count_defects == 1:
        # 2个手指的时候是这样的
        # m.click(location1[0], location1[1])
        # k.tap_key('a')
        # m.click(location2[0], location2[1])
        cv2.putText(img, "2 fingers go right ", (20, 40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(10, 10, 10))
        speed = [1, 0]

    elif count_defects == 2:
        speed = [-1, 0]
        # m.click(location1[0], location1[1])
        # k.tap_key('d')
        # m.click(location2[0], location2[1])
        cv2.putText(img, "3 fingers  go left ", (20, 40), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                    fontScale=1, color=(10, 10, 10))
    elif count_defects == 3:
        # m.click(location1[0], location1[1])
        # k.tap_key('x')
        # m.click(location2[0], location2[1])
        cv2.putText(img, "4 fingers go down", (20, 40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
                    color=(10, 10, 10))
        speed = [0, 1]

    elif count_defects == 4:
        # 五个手指的时候显示这个结果.
        # m.click(location1[0], location1[1])
        # k.tap_key('w')
        # m.click(location2[0], location2[1])
        cv2.putText(img, " 5 fingers go up  ", (20, 40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(10, 10, 10))
        speed = [0, -1]
    else:
        # 没有手势检测到的时候
        # m.click(location1[0], location1[1])
        # k.tap_key('s')
        # m.click(location2[0], location2[1])
        cv2.putText(img, "0 fingers stop! Hey! No hand detected!", (20, 40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
                    color=(10, 10, 10))
        speed = [0, 0]

    screen.fill(black)
    screen.blit(ball, ball_rect)
    pygame.display.flip()

    # show appropriate image
    cv2.imshow('Gesture', img)
    all_img = np.hstack((drawing, crop_img))
    cv2.imshow('Contours', all_img)

    k = cv2.waitKey(10)             # Esc 退出
    if k == 27:
        break
